using System;

using TrueCode.Core.Aspects;
using TrueCode.DependencyManager.BuildActions.Injection;
using TrueCode.DependencyManager.BuildActions.Instantiating;
using TrueCode.DependencyManager.BuildActions.Lifetime;
using TrueCode.DependencyManager.BuildActions.TypeMapping;
using TrueCode.DependencyManager.Extensions.Action.Typed;
using TrueCode.DependencyManager.Services.Parameters;

namespace TrueCode.DependencyManager.Extensions
{
  public static class RegistratorExtensions
  {
    public static Registrator< T > Register< T >( this Container container, object settingKey = null )
    {
      return new Registrator< T >( container, settingKey );
    }

    public static Container RegisterInstance< T >( this Container container, T instance )
    {
      container.AddActionFor< T, ISingleton >( new Singleton( instance ) );
      return container;
    }

    public static Container RegisterInstance< T >( this ExtensionBase extension, T instance )
    {
      return RegisterInstance( extension.Than, instance );
    }
  }

  public class ExtensionBase
  {
    private readonly Container _container;

    protected ExtensionBase( [NotNull] Container container )
    {
      if( container == null ) throw new ArgumentNullException( "container" );
      _container = container;
    }

    public Container Than
    {
      get { return _container; }
    }

    public Registrator< T > Register< T >( object settingKey = null )
    {
      return _container.Register< T >( settingKey );
    }

    protected Container Container
    {
      get { return _container; }
    }
  }

  public class Registrator< TImpl > : ExtensionBase
  {
    private readonly object mySettingKey;

    public Registrator( [NotNull] Container container, object settingKey = null ) : base( container )
    {
      mySettingKey = settingKey;
    }

    public Registrator< TImpl > As< TInterface >()
    {
      return As< TInterface >( true );
    }

    public Registrator< TImpl > As< TInterface >( bool checkAssignability )
    {
      Container.AddActionFor< TInterface, IMapType >( MapType.To< TImpl >( checkAssignability ), mySettingKey );
      return this;
    }

    public Registrator< TImpl > AsSingleton()
    {
      Container.AddActionFor< TImpl, ISingleton >( new Singleton(), mySettingKey );
      return this;
    }

    public Registrator< TImpl > AsSingleton< TInterface >( bool checkAssignability = false )
    {
      Container.AddActionFor< TImpl, ISingleton >( new Singleton(), mySettingKey );
      Container.AddActionFor< TInterface, IMapType >( MapType.To< TImpl >( checkAssignability ), mySettingKey );
      return this;
    }

    [NotNull]
    public Tuner Using
    {
      get { return new Tuner( this ); }
    }

    public class Tuner : ExtensionBase
    {
      private readonly Registrator< TImpl > _registrator;

      public Tuner( Registrator< TImpl > registrator ) : base( registrator.Container )
      {
        _registrator = registrator;
      }

      public Tuner Parameters( params object[] parameters )
      {
        _registrator.Container.AddServiceFor< TImpl, IParameterProvider >( new TypedParameterProvider( parameters ) );
        return this;
      }

      public Tuner FactoryMethod( Func< IBuildSession, TImpl > factoryMethod )
      {
        _registrator.Container.AddActionFor< TImpl, IInstantiate >( new FactoryMethod< TImpl >( factoryMethod ) );
        return this;
      }

      public Tuner FactoryMethod( Func< TImpl > factoryMethod )
      {
        _registrator.Container.AddActionFor< TImpl, IInstantiate >( new SimpleFactoryMethod< TImpl >( factoryMethod ) );
        return this;
      }

      public Tuner FactoryMethod( [NotNull] string methodName )
      {
        if( methodName == null ) throw new ArgumentNullException( "methodName" );
        _registrator.Container.AddActionFor< TImpl, IInstantiate >( new StaticMethodFactory< TImpl >( methodName ) );
        return this;
      }
    }
  }
}